#!/bin/sh

e2e_test_install() {
  SCRIPT="$CLUSTER_NAME-scripts"

  create_or_replace_cluster "$CLUSTER_NAME" "$CLUSTER_NAMESPACE" 1 \
    --set cluster.create=false \
    --set cluster.managedSql.scripts[0].script="CREATE DATABASE test;"

  deploy_curl_pod "$CLUSTER_NAMESPACE"

  wait_pods_running "$CLUSTER_NAMESPACE" 1

  build_default_script
}

build_default_script() {
  if kubectl get sgscripts.stackgres.io -n "$CLUSTER_NAMESPACE" "$SCRIPT"
  then
    echo "Saving Script $SCRIPT"

    DEFAULT_SCRIPT_CR="$(kubectl get sgscripts.stackgres.io -n "$CLUSTER_NAMESPACE" "$SCRIPT"  -o json \
      | jq 'del(.metadata.creationTimestamp) | del(.metadata.generation) | del(.metadata.resourceVersion) | del(.metadata.selfLink) | del(.metadata.uid)' )"

    echo_raw "$DEFAULT_SCRIPT_CR" > "$LOG_PATH/default-script-cr.json"

    DEFAULT_SCRIPT="$(cat "$LOG_PATH/default-script-cr.json" \
      | jq 'del(.apiVersion) | del(.kind)')"

    echo_raw "$DEFAULT_SCRIPT" > "$LOG_PATH/default-script.json"

    kubectl delete sgscripts.stackgres.io -n "$CLUSTER_NAMESPACE" "$SCRIPT" || true
  else
    echo "Script $SCRIPT not installed"
    return 1
  fi

}

e2e_test_after_all() {
  unset SCRIPT
}

e2e_test() {
  run_test "Check that a created script can be accessed directly through the API" check_script_directly

  run_test "Check that a created script is included in the response" check_script_in_list

  run_test "Check that a script was removed from the list of scripts after its deletion" check_script_removed_from_list

  run_test "Check that a script was removed from direct access after its deletion" check_script_deletion_directly

  run_test "Script creation through the API" test_script_create_with_api

  run_test "Check that a script created with the API is visible" api_created_script_visible

  run_test "Script update with the api" test_update_script_with_api

  run_test "Check that script changes are reflected in the api" test_api_updated_script_is_visible

  run_test "Script update with the api and mutated value" test_mutate_script_with_api

  run_test "Check that script changes are reflected in the api and mutated value" test_api_mutate_script_is_visible

  run_test "Check script deletion with api" test_delete_script_with_api

  run_test "Check that script deletions with the API are reflected in the API" test_api_delete_script_is_invible

  run_test "Constraint violations should be detected" test_constraint_violation_api_error

  run_test "User with wrong credentials should not be authenticated" test_authentication_api_error

  run_test "Create script with inline scripts" check_create_script_with_inline_script

  run_test "Create script with secret and configmap scripts" check_create_script_with_secret_and_configmap_script
}

create_script() {
  cat "$LOG_PATH/default-script-cr.json" | kubectl apply -f -

  wait_until eval 'kubectl get sgscripts.stackgres.io -n "$CLUSTER_NAMESPACE" "$SCRIPT"'
}

delete_script_only() {
  echo "Deleting script if there is any"
  if kubectl get sgscripts.stackgres.io -n "$CLUSTER_NAMESPACE" "$SCRIPT"
  then
    echo "Script $SCRIPT found, deleting..."

    kubectl delete sgscripts.stackgres.io -n "$CLUSTER_NAMESPACE" "$SCRIPT"

    wait_until eval '! kubectl get sgscripts.stackgres.io -n "$CLUSTER_NAMESPACE" "$SCRIPT"'
  else
    echo "No script found"
  fi
}

check_script_in_list() {
  create_script

  if run_curl -r "stackgres/sgscripts" -n "$CLUSTER_NAMESPACE" \
    | jq -r ".[] | select ( .metadata.namespace == \"$CLUSTER_NAMESPACE\") | select ( .metadata.name == \"$SCRIPT\") | .metadata.name" \
    | grep -q "^$SCRIPT$"
  then
    echo "Script $SCRIPT included in json response"
    return 0
  else
    echo "Script $SCRIPT not included in json response"
    return 1
  fi
}

get_script_status() {
  run_curl -r "stackgres/namespaces/$CLUSTER_NAMESPACE/sgscripts/$SCRIPT" -n "$CLUSTER_NAMESPACE" -e "-LI -o /dev/null -w %{http_code}"
}

check_script_directly() {
  local HTTP_STATUS

  create_script

  HTTP_STATUS="$(get_script_status)"

  if [ "$HTTP_STATUS" -eq "200" ]
  then
    echo "Script $SCRIPT was found by the api"
    return 0
  else
    echo "Script $SCRIPT was not found by the api"
    return 1
  fi
}

check_script_removed_from_list() {
  delete_script_only

  if run_curl -r "stackgres/sgscripts" -n "$CLUSTER_NAMESPACE" \
    | jq -r ".[] | select ( .metadata.namespace == \"$CLUSTER_NAMESPACE\") | select ( .metadata.name == \"$SCRIPT\") | .metadata.name" \
    | grep -q "^$SCRIPT$"
  then
    echo "Script $SCRIPT wasn't removed from cache"
    return 1
  else
    echo "Script $SCRIPT was removed from cache"
    return 0
  fi
}

check_script_deletion_directly() {
  local HTTP_STATUS

  delete_script_only

  HTTP_STATUS="$(run_curl -r "stackgres/namespaces/$CLUSTER_NAMESPACE/sgscripts/$SCRIPT" -n "$CLUSTER_NAMESPACE" -e "-LI -o /dev/null -w %{http_code}")"

  if [ "$HTTP_STATUS" -eq "404" ]
  then
    echo "Script $SCRIPT was not found by the api"
    return 0
  else
    echo "Script $SCRIPT was found by the api"
    return 1
  fi
}

create_script_with_api() {
  local SCRIPT_FILE="${1:-"$LOG_PATH/default-script.json"}"

  echo "Creating script $SCRIPT with the operator API"

  delete_script_only

  local HTTP_STATUS

  HTTP_STATUS="$(run_curl -r "stackgres/sgscripts" -n "$CLUSTER_NAMESPACE" -d "$SCRIPT_FILE" -e '-X POST -w %{http_code} -o /dev/null')"

  if [ "$HTTP_STATUS" = "200" ] || [ "$HTTP_STATUS" = "202" ] || [ "$HTTP_STATUS" = "204" ]
  then
    echo "Request acknowledged by the operator"
  else
    local ERROR_RESPONSE
    ERROR_RESPONSE="$(run_curl -r "stackgres/sgscripts" -n "$CLUSTER_NAMESPACE" -d "$SCRIPT_FILE" -e '-X POST')"

    echo "Invalid response status $HTTP_STATUS. response: $ERROR_RESPONSE"
    return 1
  fi
}

test_script_create_with_api() {
  create_script_with_api

  if wait_until eval 'kubectl get sgscripts.stackgres.io -n "$CLUSTER_NAMESPACE" "$SCRIPT"'
  then
    echo "Script created with the operator API"
  else
    echo "Script wasn't created with the API"
    return 1
  fi
}

api_created_script_visible() {
  create_script_with_api

  check_script_directly
}

update_script_entry_with_api() {
  local HTTP_STATUS

  UPDATED_SCRIPT="$(cat "$LOG_PATH/default-script-cr.json" \
    | jq 'del(.apiVersion) | del(.kind)' \
    | jq ".spec[\"scripts\"][$1].script = $2")"

  echo_raw "$UPDATED_SCRIPT" > "$LOG_PATH/updated-script.json"

  HTTP_STATUS="$(run_curl -r "stackgres/sgscripts" -d "$LOG_PATH/updated-script.json" -e '-X PUT -w %{http_code} -o /dev/null')"

  if [ "$HTTP_STATUS" = "200" ] || [ "$HTTP_STATUS" = "202" ] || [ "$HTTP_STATUS" = "204" ]
  then
    echo "Request acknowledged by the operator"
  else
    local ERROR_RESPONSE
    ERROR_RESPONSE="$(run_curl -r "stackgres/sgscripts" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/updated-script.json" -e '-X PUT')"
    echo "Invalid response status $HTTP_STATUS. response: $ERROR_RESPONSE"
    return 1
  fi
}

get_script_from_api() {
  run_curl -r "stackgres/namespaces/$CLUSTER_NAMESPACE/sgscripts/$SCRIPT" -n "$CLUSTER_NAMESPACE"
}

test_update_script_with_api() {
  create_script

  update_script_entry_with_api 0 '"CREATE TABLE test2;"'

  if kubectl get sgscripts.stackgres.io -n "$CLUSTER_NAMESPACE" "$SCRIPT" -o "jsonpath={.spec.scripts[0].script}" \
    | grep -q '^CREATE TABLE test2;$'
  then
    echo "Script was updated"
  else
    echo "Script was not updated"
    return 1
  fi
}

test_api_updated_script_is_visible() {
  update_script_entry_with_api 0 '"CREATE TABLE test2;"'

  if get_script_from_api | jq '.spec["scripts"][0].script' -r \
    | grep -q "CREATE TABLE test2;"
  then
    echo "Script updates are being reflected in the api"
  else
    echo "Script updates aren't being reflected in the api"
    return 1
  fi
}

test_mutate_script_with_api() {
  create_script

  update_script_entry_with_api 0 '"CREATE TABLE test3;"'

  if kubectl get sgscripts.stackgres.io -n "$CLUSTER_NAMESPACE" "$SCRIPT" -o "jsonpath={.spec.scripts[0].script}" \
    | grep -q '^CREATE TABLE test3;$'
  then
    echo "Script was updated with mutated value"
  else
    echo "Script was not updated with mutated value"
    return 1
  fi
}

test_api_mutate_script_is_visible() {
  update_script_entry_with_api 0 '"CREATE TABLE test3;"'

  if get_script_from_api | jq '.spec["scripts"][0].script' -r \
    | grep -q "CREATE TABLE test3;"
  then
    echo "Script updates are being reflected in the api with mutated value"
  else
    echo "Script updates aren't being reflected in the api with mutated value"
    return 1
  fi
}

delete_script_with_api() {
  local HTTP_STATUS

  HTTP_STATUS="$(run_curl -r "stackgres/sgscripts" -n "$CLUSTER_NAMESPACE"  -d "$LOG_PATH/default-script.json" -e '-X DELETE -w %{http_code} -o /dev/null')"

  if [ "$HTTP_STATUS" = "200" ] || [ "$HTTP_STATUS" = "202" ] || [ "$HTTP_STATUS" = "204" ]
  then
    echo "Request acknowledged by the operator"
  else
    local ERROR_RESPONSE
    ERROR_RESPONSE="$(run_curl -r "stackgres/sgscripts" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-script.json" -e '-X PUT')"
    echo "Invalid response status $HTTP_STATUS. response: $ERROR_RESPONSE"
    return 1
  fi
}

test_delete_script_with_api() {
  create_script

  delete_script_with_api

  if wait_until eval '! kubectl get sgscripts.stackgres.io -n "$CLUSTER_NAMESPACE" "$SCRIPT"'
  then
    echo "script was deleted"
  else
    echo "script was not deleted"
    return 1
  fi
}

test_api_delete_script_is_invible() {
  create_script

  delete_script_with_api

  if wait_until eval '[ "$(get_script_status)" = "404" ]'
  then
    echo "Script removed from the API"
  else
    echo "Script removed from the API"
    return 1
  fi
}

test_constraint_violation_api_error() {
  INVALID_SCRIPT="$(cat "$LOG_PATH/default-script.json" | jq 'del (.spec.scripts[0].script)' )"

  echo "$INVALID_SCRIPT" > "$LOG_PATH/invalid-script.json"

  local HTTP_STATUS

  HTTP_STATUS="$(run_curl -r "stackgres/sgscripts" -n "$CLUSTER_NAMESPACE"  -d "$LOG_PATH/invalid-script.json" -e '-X POST -w %{http_code} -o /dev/null')"

  assert_string_equal "422" "$HTTP_STATUS"

  ERROR_RESPONSE="$(run_curl -r "stackgres/sgscripts" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/invalid-script.json" -e '-X POST')"

  EXPECTED_ERROR_TYPE="constraint-violation"
  EXPECTED_ERROR_TITLE="do not comply with the syntactic rules"
  EXPECTED_ERROR_DETAIL="script and scriptFrom are mutually exclusive and required"
  EXPECTED_ERROR_FIELD="spec.scripts[0].script"

  if ! assert_api_error "$ERROR_RESPONSE"
  then
    EXPECTED_ERROR_DETAIL="Required value"
    assert_api_error "$ERROR_RESPONSE"
  fi
}

test_authentication_api_error() {
  local HTTP_STATUS

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/sgscripts"  -n "$CLUSTER_NAMESPACE" -e "-LI -o /dev/null -w %{http_code}")"

  check_authentication_error

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/namespaces/$CLUSTER_NAMESPACE/sgscript/$SCRIPT" -n "$CLUSTER_NAMESPACE" -e "-LI -o /dev/null -w %{http_code}")"

  check_authentication_error

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/sgscript" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-script.json" -e '-X POST -w %{http_code} -o /dev/null')"

  check_authentication_error

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/sgscript" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-script.json" -e '-X PUT -w %{http_code} -o /dev/null')"

  check_authentication_error

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/sgscript" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-script.json" -e '-X DELETE -w %{http_code} -o /dev/null')"

  check_authentication_error
}

check_authentication_error() {
  if [ "$HTTP_STATUS" = "401" ]
  then
    echo "Request returned expected authentication error"
    return 0
  else
    echo "Request returned unexpected response status $HTTP_STATUS instead of the expected authentication error."
    return 1
  fi
}

check_create_script_with_inline_script() {
  OPERATOR_SCRIPT_CR="$(cat "$LOG_PATH/default-script.json" \
    | jq 'del(.metadata.creationTimestamp) | del(.metadata.generation) | del(.metadata.resourceVersion) | del(.metadata.selfLink) | del(.metadata.uid)' \
    | jq 'del(.status)' \
    | jq '.spec={}' | jq '.spec.scripts=[]' \
    | jq '.spec.scripts[0].name = "init"' \
    | jq '.spec.scripts[0].script = "CREATE DATABASE test"' \
    | jq '.spec.scripts[1].name = "table"' \
    | jq '.spec.scripts[1].database = "test"' \
    | jq '.spec.scripts[1].script = "CREATE TABLE fibonacci(num integer)"')"

  echo_raw "$OPERATOR_SCRIPT_CR" > "$LOG_PATH/inline-scripts-script.json"

  create_script_with_api "$LOG_PATH/inline-scripts-script.json"

  if [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[0].name}")" = "init" ] \
    && [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[0].database}")" = "" ] \
    && [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[0].script}")" = "CREATE DATABASE test" ]
  then
    success "Init inline script was added to script configuration"
  else
    fail "Init inline script was NOT added to script configuration"
  fi

  if [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[1].name}")" = "table" ] \
    && [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[1].database}")" = "test" ] \
    && [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[1].script}")" = "CREATE TABLE fibonacci(num integer)" ]
  then
    success "Table inline script was added to script configuration"
  else
    fail "Table inline script was NOT added to script configuration"
  fi
}

check_create_script_with_secret_and_configmap_script() {
  OPERATOR_SCRIPT_CR="$(cat "$LOG_PATH/default-script.json" \
    | jq 'del(.metadata.creationTimestamp) | del(.metadata.generation) | del(.metadata.resourceVersion) | del(.metadata.selfLink) | del(.metadata.uid)' \
    | jq 'del(.status)' \
    | jq '.spec={}' \
    | jq '.spec.scripts[0].name = "init"' \
    | jq '.spec.scripts[0].scriptFrom={}' \
    | jq '.spec.scripts[0].scriptFrom.secretScript = "CREATE DATABASE test"' \
    | jq '.spec.scripts[0].scriptFrom.secretKeyRef = {}' \
    | jq '.spec.scripts[0].scriptFrom.secretKeyRef.name = "init-script"' \
    | jq '.spec.scripts[0].scriptFrom.secretKeyRef.key = "script"' \
    | jq '.spec.scripts[1].name = "table"' \
    | jq '.spec.scripts[1].database = "test"' \
    | jq '.spec.scripts[1].scriptFrom={}' \
    | jq '.spec.scripts[1].scriptFrom.configMapScript = "CREATE TABLE fibonacci(num integer)"' \
    | jq '.spec.scripts[1].scriptFrom.configMapKeyRef = {}' \
    | jq '.spec.scripts[1].scriptFrom.configMapKeyRef.name = "table-script"' \
    | jq '.spec.scripts[1].scriptFrom.configMapKeyRef.key = "script"')"

  echo_raw "$OPERATOR_SCRIPT_CR" > "$LOG_PATH/secret-and-configmap-scripts-script.json"

  create_script_with_api "$LOG_PATH/secret-and-configmap-scripts-script.json"

  if [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[0].name}")" = "init" ] \
    && [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[0].database}")" = "" ] \
    && [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[0].scriptFrom.secretKeyRef.name}")" = "init-script" ] \
    && [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[0].scriptFrom.secretKeyRef.key}")" = "script" ]
  then
    success "Secret was added to script configuration"
  else
    fail "Secret was NOT added to script configuration"
  fi

  if [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[1].name}")" = "table" ] \
    && [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[1].database}")" = "test" ] \
    && [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[1].scriptFrom.configMapKeyRef.name}")" = "table-script" ] \
    && [ "$(kubectl get sgscript -n "$CLUSTER_NAMESPACE" "$SCRIPT" \
    -o jsonpath="{.spec.scripts[1].scriptFrom.configMapKeyRef.key}")" = "script" ]
  then
    success "ConfigMap was added to script configuration"
  else
    fail "ConfigMap was NOT added to script configuration"
  fi

  OPERATOR_SCRIPT="$(get_script_from_api)"

  if [ "$(echo "$OPERATOR_SCRIPT" | jq -r ".spec.scripts[0].name")" = "init" ] \
    && [ "$(echo "$OPERATOR_SCRIPT" | jq -r ".spec.scripts[0].database")" = "null" ] \
    && [ "$(echo "$OPERATOR_SCRIPT" | jq -r ".spec.scripts[0].scriptFrom.secretKeyRef.name")" = "init-script" ] \
    && [ "$(echo "$OPERATOR_SCRIPT" | jq -r ".spec.scripts[0].scriptFrom.secretKeyRef.key")" = "script" ] \
    && [ "$(echo "$OPERATOR_SCRIPT" | jq -r ".spec.scripts[0].scriptFrom.secretScript")" = "null" ]
  then
    success "Secret was added to script configuration"
  else
    fail "Secret was NOT added to script configuration"
  fi

  if [ "$(echo "$OPERATOR_SCRIPT" | jq -r ".spec.scripts[1].name")" = "table" ] \
    && [ "$(echo "$OPERATOR_SCRIPT" | jq -r ".spec.scripts[1].database")" = "test" ] \
    && [ "$(echo "$OPERATOR_SCRIPT" | jq -r ".spec.scripts[1].scriptFrom.configMapKeyRef.name")" = "table-script" ] \
    && [ "$(echo "$OPERATOR_SCRIPT" | jq -r ".spec.scripts[1].scriptFrom.configMapKeyRef.key")" = "script" ] \
    && [ "$(echo "$OPERATOR_SCRIPT" | jq -r ".spec.scripts[1].scriptFrom.configMapScript")" = "CREATE TABLE fibonacci(num integer)" ]
  then
    success "ConfigMap was added to script configuration"
  else
    fail "ConfigMap was NOT added to script configuration"
  fi

  kubectl delete secret -n "$CLUSTER_NAMESPACE" "init-script" > /dev/null

  kubectl delete configmap -n "$CLUSTER_NAMESPACE" "table-script" > /dev/null
}
